package futbol.tacticas.concursantes.newTeam;

import static java.lang.Math.*;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Arrays;

import javax.swing.text.Position;

import futbol.Futbol;
import futbol.General;
import futbol.tacticas.ComandoGolpearBola;
/**
 * Aquesta classe es abstracte (no la podeu instanciar). I ofereix un munt de metodes estatics molt utils. Com que son estatics
 * podeu cridarlos simplement fent FutbolTools.nomDelMetode();
 * <p>
 * <p>Mireuvos-la be perque us estalviar� molta feina si la feu servir correctament (juntament amb els metodes del tecnico).
 * <p><b>nota:</b> Si teniu dubtes recorreu a en Pau o a l'Aimar
 * 
 * 
 * @author Mel & Progeny
 *
 */
public abstract class FutbolTools {

	private static Logger 					log	= Logger.getLogger(FutbolTools.class);
	
	
	
	public static PelotaAnalista calculTrajectoriaSegunPuntoDestino(Personalitat jugador, PelotaAnalista pelota, ArrayList<PelotaAnalista> trayecto, Point2D puntoDestino){
		
		double []puntoPreciso = toquePreciso(jugador,pelota,puntoDestino.getX(), puntoDestino.getY());
		PelotaAnalista pelotaHipotetica = FutbolTools.createPelotaSegunDestino(jugador, jugador.fuerza , pelota, puntoPreciso[0], puntoPreciso[1]);
		if(trayecto == null)
			trayecto = new ArrayList<PelotaAnalista>();
		FutbolTools.calculTrajectoria(pelotaHipotetica, trayecto);
		
		return FutbolTools.calculTrajectoria(pelotaHipotetica, trayecto);
	}
	
	public static PelotaAnalista calculTrajectoriaSegunJugadorDestino(Personalitat jugador, PelotaAnalista pelota, ArrayList<PelotaAnalista> trayecto, int indiceJugadorDestino){
		//Personalitat jugadorDestino = jugador.tecnico.getAlineacion()[indiceJugadorDestino];
		double []puntoPreciso = pasePreciso(jugador,pelota,indiceJugadorDestino);
		PelotaAnalista pelotaHipotetica = FutbolTools.createPelotaSegunDestino(jugador, jugador.fuerza , pelota, puntoPreciso[0], puntoPreciso[1]);
		if(trayecto == null)
			trayecto = new ArrayList<PelotaAnalista>();
		FutbolTools.calculTrajectoria(pelotaHipotetica, trayecto);
		
		return FutbolTools.calculTrajectoria(pelotaHipotetica, trayecto);
	}
	/**
	 * <b>Feu-lo servir!:</b>
	 * <p>Li passes una PelotaAnalista i un ArrayList y omple l'ArrayList amb totes PelotaAnalista que representen la
	 * trajectoria futura que far� la pilota (incloent rebots). 
	 * <p>�s util mirar l'ultima pilota de la trajectoria per veure si isPelotaConPeligroDeGol().
	 * <p>Am�s retorna l'objecte PelotaAnalista de la primera pilota amb velocitat < 15 (chutable).
	 * <p>
	 * 
	 * @param pelota
	 * @param trayecto
	 * @return
	 */
	public static PelotaAnalista calculTrajectoria(PelotaAnalista pelota, ArrayList<PelotaAnalista> trayecto){
		PelotaAnalista pT = null;
		PelotaAnalista primeraChutable = null;
		int index = 0;
		
		//Sempre creem la primera posici� de la trajectoria (encara que tingui vel=0)
		//Si te velositat 0 ja no cal que calculem m�s punts de la traject�ria
		if(trayecto==null){
			trayecto = new ArrayList<PelotaAnalista>();
		}
		trayecto.clear();
		pT=moverPelota(pelota);
		trayecto.add(index,pT); 
		index++;
		if(Math.round(pT.velocidad)<15&&primeraChutable==null){
			primeraChutable = pT.getCopia();
		}
		if(pT.isPelotaConPeligroDeGol()!=-1){
			return primeraChutable;
		}
		
		while(((int)Math.round(pT.velocidad))>0)
		{
		
			pT=moverPelota(pT);
			trayecto.add(pT);
			index++;
			if(Math.round(pT.velocidad)<15&&primeraChutable==null){
				primeraChutable = pT.getCopia();
			}
			if(pT.isPelotaConPeligroDeGol()!=-1){
				return primeraChutable;
			}
		}

		//No hay ninguna pelota chutable
		return primeraChutable;
	}
	


	/**
	 * <b>No la feu servir!</b>
	 * <p>S'utilitza en el calcul d'una trajectoria.
	 * <p>Retorna la <b>"pelotaOrigen"</b> despues d'averla mogut. (Te en compte rebots y esGol()).
	 * <p>
	 * @param pelotaOrigen
	 * @return
	 */
	private static PelotaAnalista  moverPelota(PelotaAnalista pelotaOrigen)
	{
		boolean rebote = false;

		PelotaAnalista p = new PelotaAnalista(pelotaOrigen.x,pelotaOrigen.y, pelotaOrigen.velocidad,pelotaOrigen.angulo);
		PelotaAnalista posiblePuntoDeGol = p.getCopia();
		
		for (int n=0;n<(int)Math.round(p.velocidad)&&Tecnico.esGol(p)==-1;n++){
			p.x+=Math.cos(p.angulo);
			p.y+=Math.sin(p.angulo);
			
			if (Tecnico.esGol(p)==-1){
				if (p.x>160){
					p.x=320-p.x;  //160 - (x-160)
					p.angulo=General.corregirAngulo(Math.PI-p.angulo);
					rebote = true;
				}
				if (p.x<-160){
					p.x=-320-p.x; //-160 + (-160-x)
					p.angulo=General.corregirAngulo(Math.PI-p.angulo);
					rebote = true;
				}
				if (p.y>120){
					p.y=240-p.y; //120 - (x-120)
					p.angulo=General.corregirAngulo(-p.angulo);
					rebote = true;
				}
				if (p.y<-120){
					p.y=-240-p.y;  //-120 + (-120-x)
					p.angulo=General.corregirAngulo(-p.angulo);
					rebote = true;
				}
				
				
			}
		}
	
		if ((int)Math.round(p.velocidad)>0) p.velocidad--;
		
		if(Tecnico.esGol(p)!=-1){
			posiblePuntoDeGol.setPelotaConPeligroDeGol(Tecnico.esGol(p));
			posiblePuntoDeGol.setHaRebotado(rebote);
			return posiblePuntoDeGol.getCopia();
		}
		
		p.setHaRebotado(rebote);
		
		return p;
		
	}
	
	
	
	//// Funciones de mejora del juego de las Personalidades
	
	/**
	 * Retorna la for�a efectiva que s'aplicar� a la pilota en funci� de la <b>fuerzaDelJugador</b> i de les
	 * coordenades de dest�.
	 * <p>
	 * @param fuerzaDelJugador
	 * @param posicionJugadorOrigen
	 * @param xDestino
	 * @param yDestino
	 * @return Int amb la For�a efectiva que s'aplicar� a la pilota en funci� de la <b>fuerzaDelJugador</b> i de les
	 * coordenades de dest�.
	 */
	public static  int getFuerzaEfectivaSegunDestino(int fuerzaDelJugador, Point2D posicionJugadorOrigen,  double xDestino, double yDestino){		
	
		double distancia = posicionJugadorOrigen.distance(xDestino,yDestino);
		double fuerza = fuerzaDelJugador;
		int nGrado;
		
		
		if(distancia<50){
			nGrado = (int)(distancia*2);
			int fuerza_efectiva = (int)(fuerza*nGrado/100);
			return fuerza_efectiva;
		}
		
		return fuerzaDelJugador;
	}
	
	
	/**
	 * Retorna la velocitat a la que surt disparada la pilota segons la for�a aplicada i les coordenades de dest�.
	 * <p>
	 * @param jugadorOrigen
	 * @param xDestino
	 * @param yDestino
	 * @return Int amb la velocitat a la que surt disparada la pilota segons la for�a aplicada i les coordenades de dest�.
	 */
	public static int getVelocidadEfectivaSegunDestino(Point2D jugadorOrigen, int fuerzaJugador, double xDestino, double yDestino){
		
		return 3*getFuerzaEfectivaSegunDestino(fuerzaJugador, jugadorOrigen,  xDestino,  yDestino);
	}
	
	
	/**
	 * Retorna la velocitat a la que surt disparada la pilota segons la for�a aplicada i les coordenades de dest�.
	 * <p>
	 * @param fuerzaDelJugador
	 * @param posicionJugadorOrigen
	 * @param xDestino
	 * @param yDestino
	 * @return Int amb la velocitat a la que surt disparada la pilota segons la for�a aplicada i les coordenades de dest�.
	 */
	/*
	public static int getVelocidadEfectivaSegunDestino(int fuerzaDelJugador, Point2D posicionJugadorOrigen, double xDestino, double yDestino){
		
		return 3*getFuerzaEfectivaSegunDestino(fuerzaDelJugador, posicionJugadorOrigen,  xDestino,  yDestino);
	}
	*/
	
	/**
	 * Retorna una PelotaAnalista que te els valors de Velocitat y Angulo calculats per si volguessim xutar la pilota <b>"pelotaOrigen"</b> amb un tio colocat 
	 * a <b>"jugadorOrigen.getPoint()"</b> amb for�a <b>"jugadorOrigen.getFuerza()"</b> cap al punt <b>"(xDestino, yDestino)"</b>.
	 * <p>El metode es una mica llest, i en funci� de la distancia on chutes ja et pondera la for�a i aplica la resultant a la pilota.
	 * <p>
	 * <p>Aquest metode s'utilitza a l'algoritme de pasePreciso() pero podeu fer-lo servir igualment.
	 * <p>
	 * @param jugadorOrigen - objecte Personalitat del jugador que xuta
	 * @param pelotaOrigen - PelotaAnalista de la pelota origen (tecnico.getMiPelota())
	 * @param xDestino - Cooredenada X del punt al que vols fer el xut.
	 * @param yDestino - Cooredenada Y del punt al que vols fer el xut.
	 * @return PelotaAnalista creada segun parametros sobre JugadorOrigen y el destino.
	 */
	public static PelotaAnalista createPelotaSegunDestino(Point2D jugadorOrigen, int fuerzaJugador, PelotaAnalista pelotaOrigen,  double xDestino, double yDestino){
		PelotaAnalista pelota = null;
		
		
		double angulo = calcAngulo(pelotaOrigen, xDestino, yDestino);
		
		pelota = new PelotaAnalista(pelotaOrigen.x, pelotaOrigen.y , getVelocidadEfectivaSegunDestino(jugadorOrigen, fuerzaJugador, xDestino,  yDestino),angulo);
		
		return pelota;
	}

	 /**
	  * <b>Feu-la servir.</b>
	  * <p>Utilitat que serveix per calcular angles donats dos punts (definits per dos Point2D).
	  * @param xOrigen
	  * @param yOrigen
	  * @param xDestino
	  * @param yDestino
	  * @return Angulo resultante
	  */
	 public static double calcAngulo(Point2D pOrigen, Point2D pDestino){
		 return calcAngulo(pOrigen.getX(), pOrigen.getY(), pDestino.getX(), pDestino.getY());
	 }
	
	 /**
	  * <b>Feu-la servir.</b>
	  * <p>Utilitat que serveix per calcular angles donats dos punts (definits per un Point2D i una coordenada (x,y)).
	  * @param xOrigen
	  * @param yOrigen
	  * @param xDestino
	  * @param yDestino
	  * @return Angulo resultante
	  */
	 public static double calcAngulo(Point2D pelotaOrigen , double xDestino, double yDestino){
		 
		 return calcAngulo(pelotaOrigen.getX(), pelotaOrigen.getY(), xDestino, yDestino);
	 }
	 
	 /**
	  * <b>Feu-la servir.</b>
	  * <p>Utilitat que serveix per calcular angles donats dos punts (definits per coordenades (x,y)).
	  * @param xOrigen
	  * @param yOrigen
	  * @param xDestino
	  * @param yDestino
	  * @return Angulo resultante
	  */
	 public static double calcAngulo(double xOrigen, double yOrigen , double xDestino, double yDestino){
		 	double vectorX = xDestino-xOrigen;
			double vectorY = yDestino-yOrigen;

			if(((xDestino==xOrigen)&&(yDestino==yOrigen))&&xDestino==0){
				return 0;
			}
			if(vectorX==0&&vectorY==0){
				return 0;
			}
			
			return General.corregirAngulo(Math.atan2(vectorY,vectorX));
	 }
	 
	 public static double calcAnguloPI(double xOrigen, double yOrigen , double xDestino, double yDestino){
		 	double vectorX = xDestino-xOrigen;
			double vectorY = yDestino-yOrigen;

			if(((xDestino==xOrigen)&&(yDestino==yOrigen))&&xDestino==0){
				return 0;
			}
			if(vectorX==0&&vectorY==0){
				return 0;
			}
			
			return Math.atan2(vectorY,vectorX);
	 }
	 
	 public static double corregirAnguloPI(double ang){
	        while (ang<0)
	            ang+=Math.PI*2;
	        while (ang>=Math.PI*2)
	            ang-=Math.PI*2;
	        return ang;
	    }
	 

	 
	 /**
	  * Es una copia exacte del metode estaEnVector() de la classe SituacionJugadores 
	  * (pero all� era privada i no es podia cridar). Serveix per sapiguer si el valor "num"
	  * apareix dins de "vector[]".
	  * <p>
	  * @param vector
	  * @param num
	  * @return - retorna true si "vector[]" cont� "num". retorna false en cas contrari.
	  */
	 public static boolean estaEnVector(int vector[], int num){
		 if (vector==null) return false;
		 for (int n=0;n<vector.length;n++)
			 if (vector[n]==num)
				 return true;
		 return false;
	 }
	 
	 
	 /**
	  * <b>Feulo servir!</b>
	  * <p>Li dones la for�a d'un jugador i et torna des de quina dist�ncia els seus xuts son imparables.
	  * Es el metode que es fa servir per pintar les "lines grises" de la "zona critica".
	  * <p>
	  * @param fuerza - For�a del jugador que xuta.
	  * @return
	  */
	 public static Double getDistanciaCritica(int fuerza) {
		 switch (fuerza) {
		 case 7:
			 return 126.0;
		 case 6:
			 return 66.0;
		 case 5:
			 return 15.0;
		 default:
			 return 0.0;
		 }
	 }
	 
	 
	 /**
	  * <b>No l'haurieu de fer servir.</b> 
	  * <p>S'utilitza des de l'algoritme calculaPuntoPreciso();
	  * Utilitat que permet resoldre una ecuaci� de segon grau amb 2 resultats. 
	  * I amb la condici� de que l'arrel sigui un real (no un complex).
	  * <p>
	  * @param A
	  * @param B
	  * @param C
	  * @return
	  */
	 private static double[] calculEcuacio2nGrau(double A, double B, double C){
		 double [] x = new double[2];
		 double arrel;
		 
		 arrel = Math.sqrt(B*B-4*A*C);
		 
		 if(Double.isInfinite(arrel))
			 log.error("arrel es Infinite");

		 if(Double.isNaN(arrel))
			 log.error("arrel es NaN");
		 
		 
		 x[0] = (-B+arrel)/(2*A);
		 x[1] = (-B-arrel)/(2*A);
		 
		 return x;
	 }

	 
		/**
		 * <b>Feu-lo servir!.</b>
		 * <p>Serveix per fer PasesPrecisos. Un pase preciso �s el que te la primera pilotaChutable (vel<15) just 
		 * en el punt on voleu passar-la, on voleu que la rebin. Per tant, aquest metode retorna les coordenades X i Y 
		 * que haureu de passarli a new ComandoGolpearBola(indexJugador, X, Y) per fer un pase preciso a (xDestino, yDestino).
		 * <p>
		 * 
		 * @param jugadorOrigen - Jugador que xuta
		 * @param pelotaOrigen - Pilota que xuta (tecnico.getMiPelota())
		 * @param xDestino - Cooredenada X del punt al que vols fer el pasePreciso.
		 * @param yDestino - Cooredenada Y del punt al que vols fer el pasePreciso.
		 * @return - Coordenades x i y resultants en format double[2];
		 */
		public static double [] pasePreciso(Personalitat jugadorOrigen, PelotaAnalista pelotaOrigen , int indexJugadorDestion)
		{
			Tecnico tecnico = jugadorOrigen.tecnico;
			double [] pasePreciso = new double[2];
			int fuerzaDelJugador = jugadorOrigen.getFuerza();
			
			double xDestino = tecnico.getUltimaSJ().getMisJugadores()[indexJugadorDestion].getX();
			double yDestino = tecnico.getUltimaSJ().getMisJugadores()[indexJugadorDestion].getY();
			
			pasePreciso[0] = xDestino;
			pasePreciso[1] = yDestino;
			
			
			ArrayList<PelotaAnalista> trayectoriaHipotetica = new ArrayList<PelotaAnalista>();
			PelotaAnalista pelotaHipotetica = null;
			PelotaAnalista pelotaChutableHipotetica = null;
			
			int fuerzaIdeal = 0;
			int fuerzaHipotetica = 0;
			int fuerzaReducida = 0;
			double error_minimo = 9999;
			double error_actual = 0;
			
			fuerzaIdeal=fuerzaHipotetica=fuerzaDelJugador;
			
			double anguloHipoteticoPel = calcAngulo(pelotaOrigen,xDestino,yDestino);
			
			//Trimeado anti error
			/*if(anguloHipoteticoPel%(Math.PI/2)==0){
				log.debug("entra per angle = "+Math.toDegrees(anguloHipoteticoPel));
				//anguloHipoteticoPel += 0.000001;
			}
			*/
			
			int xMaximo = (int)Math.round(pelotaOrigen.x+60*Math.cos(anguloHipoteticoPel));
			int yMaximo = (int)Math.round(pelotaOrigen.y+60*Math.sin(anguloHipoteticoPel));
			
			pelotaHipotetica = createPelotaSegunDestino(jugadorOrigen, jugadorOrigen.getFuerza() ,pelotaOrigen,   xMaximo,  yMaximo);
			pelotaChutableHipotetica = calculTrajectoria(pelotaHipotetica,trayectoriaHipotetica);
			
			if(pelotaChutableHipotetica == null){
				return pasePreciso;
			}
			
			error_minimo = pelotaChutableHipotetica.distance(xDestino,yDestino);

		
			
			//log.debug("Pase Preciso");
			for(fuerzaHipotetica--; fuerzaHipotetica>0&& error_actual<=error_minimo; fuerzaHipotetica--){
				
				//log.debug("fuerzaHipotetica: "+fuerzaHipotetica);
				
				pelotaHipotetica = createPelotaSegunDestino(jugadorOrigen, fuerzaHipotetica,  pelotaOrigen,   xMaximo,  yMaximo);
				pelotaChutableHipotetica = calculTrajectoria(pelotaHipotetica,trayectoriaHipotetica);
				error_actual = pelotaChutableHipotetica.distance(xDestino,yDestino);
				
				if(error_actual<error_minimo){
					error_minimo=error_actual;
					fuerzaIdeal = fuerzaHipotetica;
				}
			}
			
			boolean contrariEnTrajectoria = true;
			for(fuerzaHipotetica=fuerzaIdeal ; fuerzaHipotetica>0&& contrariEnTrajectoria; fuerzaHipotetica--){
			
				contrariEnTrajectoria = tecnico.contrariEnTrajectoriaPuntoPreciso(jugadorOrigen, fuerzaHipotetica, pelotaOrigen, indexJugadorDestion)<1;
				//log.debug("ventaja: "+tecnico.contrariEnTrajectoria(jugadorOrigen, pelotaHipotetica, indexJugadorDestion));
			
				if(!contrariEnTrajectoria){
					fuerzaReducida = fuerzaHipotetica;
				}
			}
			
			if(!contrariEnTrajectoria){
				fuerzaIdeal = fuerzaReducida;
			}
			
			//log.debug("Fuerza Elejida = "+fuerzaIdeal);
			//log.debug("");
			
			int offset = 1;
			double distanciaRealJugador = 55; //Distancia per for�a total =  50 per curarnos en salud --> 51
			if(fuerzaIdeal<fuerzaDelJugador){
				distanciaRealJugador = ((double)fuerzaIdeal/(double)fuerzaDelJugador)*50+offset;
				distanciaRealJugador = trimDistanciasPosibles(distanciaRealJugador);
				
			}
			
			
			pasePreciso = calculaPuntoPreciso(jugadorOrigen.getPointAcutal(), pelotaOrigen.getPoint2D(), xDestino, yDestino, distanciaRealJugador);
			
			double a = Math.toDegrees(anguloHipoteticoPel);
			double b = Math.toDegrees(calcAngulo(pelotaOrigen, pasePreciso[0], pasePreciso[1]));
			
			
			if((Math.floor(a))!=(Math.floor(b))){
			//	log.error("Angulo deseado1:"+Math.toDegrees(anguloHipoteticoPel)+"   radio:"+distanciaRealJugador);
			//	log.error("Angulo deseado2:"+Math.toDegrees(calcAngulo(pelotaOrigen, pasePreciso[0], pasePreciso[1])));
			//	double anguloJugador = calcAngulo(jugadorOrigen,xDestino,yDestino);
			//	log.error("Angulo jugador:"+Math.toDegrees(anguloJugador)+"\n");
			}
			
			
			//////////////// Comprovar que funciona
			//   if(fuerzaIdeal==getFuerzaEfectivaSegunDestino(jugadorOrigen.getFuerza(), jugadorOrigen.getPointAcutal(), pasePreciso[0],  pasePreciso[1] )){
			//      double dist = jugadorOrigen.distance(pasePreciso[0],pasePreciso[1]);
			// 	     log.debug("FUNCIONA! -\tJUG: "+jugadorOrigen.getNumero()+"\tfuerzaIdeal="+fuerzaIdeal+"\tdist: "+dist);
			//   }else{
			//  	 log.debug("NOOOOOOOOOOO! FUERZAIDEAL="+fuerzaIdeal+" fuerzaReal="+getFuerzaEfectivaSegunDestino(jugadorOrigen.getFuerza(), jugadorOrigen.getPointAcutal(), pasePreciso[0],  pasePreciso[1] ));
			//   }
			//////////////// 
			
			
			return pasePreciso;
		}
		
		
		/**
		 * <b>Feu-lo servir!.</b>
		 * <p>Serveix per fer PasesPrecisos. Un pase preciso �s el que te la primera pilotaChutable (vel<15) just 
		 * en el punt on voleu passar-la, on voleu que la rebin. Per tant, aquest metode retorna les coordenades X i Y 
		 * que haureu de passarli a new ComandoGolpearBola(indexJugador, X, Y) per fer un pase preciso a (xDestino, yDestino).
		 * <p>
		 * 
		 * @param jugadorOrigen - Jugador que xuta
		 * @param pelotaOrigen - Pilota que xuta (tecnico.getMiPelota())
		 * @param xDestino - Cooredenada X del punt al que vols fer el pasePreciso.
		 * @param yDestino - Cooredenada Y del punt al que vols fer el pasePreciso.
		 * @return - Coordenades x i y resultants en format double[2];
		 */
		public static double [] toquePreciso(Personalitat jugadorOrigen, PelotaAnalista pelotaOrigen , double xDestino, double yDestino)
		{
			double [] pasePreciso = new double[2];
			int fuerzaDelJugador = jugadorOrigen.getFuerza();
			
			pasePreciso[0] = xDestino;
			pasePreciso[1] = yDestino;
			
			
			ArrayList<PelotaAnalista> trayectoriaHipotetica = new ArrayList<PelotaAnalista>();
			PelotaAnalista pelotaHipotetica = null;
			PelotaAnalista pelotaChutableHipotetica = null;
			
			int fuerzaIdeal = 0;
			int fuerzaHipotetica = 0;
			int fuerzaReducida = 0;
			double error_minimo = 9999;
			double error_actual = 0;
			
			fuerzaIdeal=fuerzaHipotetica=fuerzaDelJugador;
			
			double anguloHipoteticoPel = calcAngulo(pelotaOrigen,xDestino,yDestino);
			
			//Trimeado anti error
			/*if(anguloHipoteticoPel%(Math.PI/2)==0){
				log.debug("entra per angle = "+Math.toDegrees(anguloHipoteticoPel));
				//anguloHipoteticoPel += 0.000001;
			}
			*/
			
			int xMaximo = (int)Math.round(pelotaOrigen.x+60*Math.cos(anguloHipoteticoPel));
			int yMaximo = (int)Math.round(pelotaOrigen.y+60*Math.sin(anguloHipoteticoPel));
			
			pelotaHipotetica = createPelotaSegunDestino(jugadorOrigen, jugadorOrigen.fuerza ,pelotaOrigen,   xMaximo,  yMaximo);
			pelotaChutableHipotetica = calculTrajectoria(pelotaHipotetica,trayectoriaHipotetica);
			
			if(pelotaChutableHipotetica == null){
				return pasePreciso;
			}
			
			error_minimo = pelotaChutableHipotetica.distance(xDestino,yDestino);
			
			//log.debug("Toque Preciso");
			for(fuerzaHipotetica--; fuerzaHipotetica>0&& error_actual<=error_minimo; fuerzaHipotetica--){
				
				//log.debug("fuerzaHipotetica: "+fuerzaHipotetica);
				
				pelotaHipotetica = createPelotaSegunDestino(jugadorOrigen, fuerzaHipotetica,  pelotaOrigen,   xMaximo,  yMaximo);
				pelotaChutableHipotetica = calculTrajectoria(pelotaHipotetica,trayectoriaHipotetica);
				error_actual = pelotaChutableHipotetica.distance(xDestino,yDestino);
				
				if(error_actual<error_minimo){
					error_minimo=error_actual;
					fuerzaIdeal = fuerzaHipotetica;
				}
			
			}
			//log.debug("");
			
			int offset = 1;
			double distanciaRealJugador = 55; //Distancia per for�a total =  50 per curarnos en salud --> 51
			if(fuerzaIdeal<fuerzaDelJugador){
				distanciaRealJugador = ((double)fuerzaIdeal/(double)fuerzaDelJugador)*50+offset;
				distanciaRealJugador = trimDistanciasPosibles(distanciaRealJugador);
				
			}
			
			
			pasePreciso = calculaPuntoPreciso(jugadorOrigen.getPointAcutal(), pelotaOrigen.getPoint2D(), xDestino, yDestino, distanciaRealJugador);
			
			double a = Math.toDegrees(anguloHipoteticoPel);
			double b = Math.toDegrees(calcAngulo(pelotaOrigen, pasePreciso[0], pasePreciso[1]));
			
			
			if((Math.floor(a))!=(Math.floor(b))){
			//	log.error("Angulo deseado1:"+Math.toDegrees(anguloHipoteticoPel)+"   radio:"+distanciaRealJugador);
			//	log.error("Angulo deseado2:"+Math.toDegrees(calcAngulo(pelotaOrigen, pasePreciso[0], pasePreciso[1])));
			//	double anguloJugador = calcAngulo(jugadorOrigen,xDestino,yDestino);
			//	log.error("Angulo jugador:"+Math.toDegrees(anguloJugador)+"\n");
			}
			
			
			//////////////// Comprovar que funciona
			//   if(fuerzaIdeal==getFuerzaEfectivaSegunDestino(jugadorOrigen.getFuerza(), jugadorOrigen.getPointAcutal(), pasePreciso[0],  pasePreciso[1] )){
			//      double dist = jugadorOrigen.distance(pasePreciso[0],pasePreciso[1]);
			// 	     log.debug("FUNCIONA! -\tJUG: "+jugadorOrigen.getNumero()+"\tfuerzaIdeal="+fuerzaIdeal+"\tdist: "+dist);
			//   }else{
			//  	 log.debug("NOOOOOOOOOOO! FUERZAIDEAL="+fuerzaIdeal+" fuerzaReal="+getFuerzaEfectivaSegunDestino(jugadorOrigen.getFuerza(), jugadorOrigen.getPointAcutal(), pasePreciso[0],  pasePreciso[1] ));
			//   }
			//////////////// 
			
			
			return pasePreciso;
		}
		
	 
	 
	 

	 /**
	  * <b>Aquest metode no l'heu de fer servir.</b>
	  * <p> S'utilitza dins l'algoritme de pasePreciso();
	  * <p>
	  * @param puntoJugador - Point2D de la posici� actual del jugador que fa el pase.
	  * @param puntoPelota - Point2D de la posici� de la pilota actua (tecnico.getMiPelota();)
	  * @param xDestino - coordenada X del punt on volem fer el pase preciso.
	  * @param yDestino - coordenada Y del punt on volem fer el pase preciso.
	  * @param radio - Dist�ncia entre el jugador que fa el pase y el punt on fem el pase (es calcula dins de pasePreciso())
	  * @return - El punt perfecte per fer el pase preciso.
	  */
	private static double[] calculaPuntoPreciso(Point2D puntoJugador, Point2D puntoPelota, double xDestino, double yDestino, double radio){
		double [] puntoPreciso = new double[2];
		double [] xAux = null;
		double [] point0 = new double[2];
		double [] point1 = new double[2];
		double M,A,a,b,r, Q, R, S,v,h,error0,error1;
		
		
		if(radio<puntoPelota.distance(puntoJugador)){
			//log.debug("no hay que hacer nada");
			puntoPreciso[0] = puntoPelota.getX();
			puntoPreciso[1] = puntoPelota.getY();
			return puntoPreciso;
		}
		
		
		a = puntoJugador.getX();
		b = puntoJugador.getY();
		r = radio;
		
		v = yDestino - puntoPelota.getY();
		h = xDestino - puntoPelota.getX();
		if(Double.isNaN(v))
			log.error("v is NaN");
		if(Double.isNaN(h))
			log.error("h is NaN");
		
		M = v/h;
		if(Double.isNaN(M)){
			//log.error("M is NaN");
			M=0;
		}
		if(Double.isInfinite(M)){
			//log.error("M is Infinite --> v:"+v+" h:"+h);
			M=999999;
		}
		
		A = puntoPelota.getY() - (puntoPelota.getX()*M);

		if(Double.isNaN(A))
			log.error("A is NaN");
		
		Q = (1+(M*M));
		R = 2*(M*A-M*b-a);
		S = (b*b+a*a+A*A-(r*r)-(2*A*b));
		
		if(Double.isNaN(Q))
			log.error("Q is NaN");
		if(Double.isNaN(R))
			log.error("R is NaN");
		if(Double.isNaN(S))
			log.error("S is NaN");
		
		
		xAux = calculEcuacio2nGrau(Q,R,S);
		
	
	
		
		point0[0] = xAux[0];
		point0[1] = (M*xAux[0]+A);
		point1[0] = xAux[1];
		point1[1] = (M*xAux[1]+A);
		
		if(Double.isNaN(xAux[0])&&Double.isNaN(xAux[1])){
			log.error(" point0["+(int)point0[0]+","+(int)point0[1]+"]");
			log.error(" point1["+(int)point1[0]+","+(int)point1[1]+"]");
		}
		
		double anguloDeseado = calcAngulo(puntoPelota, xDestino, yDestino);
		
			
		
		error0 = Math.abs(anguloDeseado-calcAngulo(puntoPelota,point0[0],point0[1]));
		error1 = Math.abs(anguloDeseado-calcAngulo(puntoPelota,point1[0],point1[1]));
		
		if(Double.isNaN(xAux[0])&&Double.isNaN(xAux[1])){
			log.error("xAux is NaN");
			log.error("angul_deseado: "+toDegrees(anguloDeseado));
			log.error("pilota: ["+puntoPelota.getX()+","+puntoPelota.getY()+"]   destino: ["+xDestino+","+yDestino+"]    jugador: ["+puntoJugador.getX()+","+puntoJugador.getY()+"]");
			log.error("error0="+error0+" ang:"+Math.toDegrees(calcAngulo(puntoPelota,point0[0],point0[1]))+ " point0["+(int)point0[0]+","+(int)point0[1]+"]");
			log.error("error1="+error1+" ang:"+Math.toDegrees(calcAngulo(puntoPelota,point1[0],point1[1]))+ " point1["+(int)point1[0]+","+(int)point1[1]+"]  radio:"+radio);
		}
		
		if(error0>0.00001&&error1>0.00001){
			log.error("angul_deseado: "+anguloDeseado);
			log.error("error0="+error0+" ang:"+Math.toDegrees(calcAngulo(puntoPelota,point0[0],point0[1]))+ " point0["+(int)point0[0]+","+(int)point0[1]+"]");
			log.error("error1="+error1+" ang:"+Math.toDegrees(calcAngulo(puntoPelota,point1[0],point1[1]))+ " point1["+(int)point1[0]+","+(int)point1[1]+"]");
			log.error("pilota: ["+puntoPelota.getX()+","+puntoPelota.getY()+"]   destino: ["+xDestino+","+yDestino+"]    jugador: ["+puntoJugador.getX()+","+puntoJugador.getY()+"]  radio:"+radio+" dist:"+puntoJugador.distance(puntoPelota));
		}
		
		if((error0<error1)&&(error0<0.00001)){
			puntoPreciso = point0;
		}else if((error1<error0)&&(error1<0.00001)){
			puntoPreciso = point1;
		}else{
			
			error0 = Math.hypot((xDestino-point0[0]),(yDestino-point0[1]));
			error1 = Math.hypot((xDestino-point1[0]),(yDestino-point1[1]));
			log.error("Decision x distancia");
			
			if(error0<error1){
				puntoPreciso = point0;
				log.error("Gana point 0");
			}else if(error1<error0){
				puntoPreciso = point1;
				log.error("Gana point 0");
			}else{
				puntoPreciso = point0;
			}

		}
		
		error1 = Math.abs(anguloDeseado-calcAngulo(puntoPelota,puntoPreciso[0],puntoPreciso[1]));
		if(error1>0.00001){
			log.error("NOOOO - anguloDeseado:"+Math.toDegrees(anguloDeseado)+" angulo_final="+Math.toDegrees(calcAngulo(puntoPelota,puntoPreciso[0],puntoPreciso[1]))+"\n");
			
			
			//log.error("point0: ("+point0[0]+","+point0[1]+")");
			//log.error("point0: ("+point1[0]+","+point1[1]+")");
			//log.error("puntoPreciso: ("+puntoPreciso[0]+","+puntoPreciso[1]+")");
			
		}
		
		return puntoPreciso;
	}
	
	/**
	 * <b>Aquest metode no l'heu de fer servir.</b> 
	 * <p>Es part de l'algoritme de pasePreciso();.  
	 * <p>
	 * @param distanciaRealJugador - Dist�ncia exacte a la que ha d'estar el jugador ha d'estar del punt on volem xutar pq el xut sigui de PM. 
	 * @return double - Retorna la distanciaRealJugador a dist�ncia ajustada a valors que no provocar�n fallos de precisi� a l'algoritme.
	 */
	private static double trimDistanciasPosibles(double distanciaRealJugador){
		if(distanciaRealJugador>42.85&&distanciaRealJugador<44){
			//6/7
			distanciaRealJugador=44;
		}
		
		if(distanciaRealJugador>41.667&&distanciaRealJugador<42.5){
			//5/6
			distanciaRealJugador=42.5;
		}
		
		if(distanciaRealJugador>40&&distanciaRealJugador<41){
			//4/5
			distanciaRealJugador=41;
		}
		
		if(distanciaRealJugador>35.72&&distanciaRealJugador<38){
			//5/7
			distanciaRealJugador=38;
		}
		if(distanciaRealJugador>33.334&&distanciaRealJugador<34){
			//4/6
			distanciaRealJugador=34;
		}
		
		return distanciaRealJugador;
	}
	
	public static int distanciaMaximaDePelota(int fuerzaDeJugador){
		int distancia = 0;
		for(int i=0; i<=fuerzaDeJugador*3; i++)
			distancia+=i;
		
		return distancia;
	}
	
	
	public static int turnosHastaPunto(Point2D puntoJugador, int velocidad, Point2D puntoDestino){
		return turnosHastaPunto(puntoJugador, velocidad, puntoDestino, 10);
	}
	
	public static int turnosHastaPunto(Point2D puntoJugador, int velocidad, Point2D puntoDestino, int humbral){
		return (int) (puntoJugador.distance(puntoDestino)-humbral)/ velocidad;
	}
	
	public static int turnosHastaPunto(double jx, double jy, int velocidad, double px, double py){
		return turnosHastaPunto(new Point2D.Double(jx,jy), velocidad, new Point2D.Double(px,py));
	}
	public static double[] arrayCopy (double[] original, int newLength) {
        double[] copy = new double[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
	public static int[] arrayCopy (int[] original, int newLength) {
        int[] copy = new int[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
	public static Juegaytoma[] arrayCopy (Juegaytoma[] original, int newLength) {
		Juegaytoma[] copy = new Juegaytoma[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
	
	public static double[][] arrayCopy (double[][] original, int newLength) {
        double[][] copy = new double[newLength][];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
	public static double aA(double angulo, double anguloDeseado){
		
		while(angulo>anguloDeseado+PI){
			angulo -= 2*PI;
		}
		
		while(angulo<anguloDeseado-PI){
			angulo += 2*PI;
		}

		return angulo;
	}
	/*public static double aA(double angulo1, double angulo2){
		double angulo1Menos = angulo1-2*PI;
		double angulo1Mas =  angulo1-2*PI;
		
		double difMas = Math.abs(angulo1Mas-angulo2);
		double dif = Math.abs(angulo1-angulo2);
		double difMenos = Math.abs(angulo1Menos-angulo2);
			
		
		if(difMas<dif){
			if(difMas<difMenos)
			 	return angulo1Mas;
		}
		
		if(dif<difMas){
			if(dif<difMenos)
				return angulo1;
		}
		
		return angulo1Menos;
	}*/
	/*
	public static double aA(double angulo1, double angulo2){

		if(getCuadrante(angulo1)==1&&getCuadrante(angulo2)==4){
			while((angulo2-angulo1)>PI){
				angulo1 += 2*PI;
			}
		}
		
		
		if(getCuadrante(angulo2)==1&&getCuadrante(angulo1)==4){
			while((angulo1-angulo2)>PI){
				angulo1 -= 2*PI;
			}
			
		}
		
		return angulo1;
	}
	*/
	public static int getCuadrante(double angulo){
		
		if(sin(angulo)>=0&&cos(angulo)>=0){
			return 1;
		}else if(sin(angulo)>=0&&cos(angulo)<0){
			return 2;
		}else if(sin(angulo)<0&&cos(angulo)<0){
			return 3;
		}else if(sin(angulo)<0&&cos(angulo)>=0){
			return 4;
		}
		
		return 1;
	
	}
	public static double[] toquePrecisoSinParedes(Personalitat jugadorOrigen, PelotaAnalista bolaHipotetica, Point2D puntoDestino){
		double offset = 0;
		double p[]=null;
		
		if(puntoDestino!=null)
		{
			if(bolaHipotetica.getY()<0){
				offset = -(120+puntoDestino.getY());
			}else{
				offset = 120 - puntoDestino.getY();
			}
			jugadorOrigen.y += offset;
			bolaHipotetica.y += offset;
			p = FutbolTools.toquePreciso(jugadorOrigen, bolaHipotetica, puntoDestino.getX(), puntoDestino.getY()+offset);
			jugadorOrigen.y -= offset;
			bolaHipotetica.y -= offset;
			p[1] -= offset;
		}
		return p;
	}
	public static double[][] getPosicionesIniciales(int numJugadores)
	{
		double[][] posicionesIniciales = new double[numJugadores][2];
		
		
		
		
		
		return posicionesIniciales;
	}
}
